#!/usr/bin/env python
# -*- coding: utf8 -*-

'''
Created on 2011-7-26

图片识别模块

@author: zhongfeng
'''

import ImageFilter, ImageChops
import Image
import re,itertools
import time

try:
    import psyco
    psyco.full()
except ImportError:
    pass

class CaptchaAlgorithm(object):
    '''captcha algorithm'''
    def LevenshteinDistance(self, m, n):
        c = [[i] for i in range(0, len(m) + 1)]
        c[0] = [j for j in range(0, len(n) + 1)]

        for i in range(0, len(m)):
            for j in range(0, len(n)):
                c[i + 1].append(
                    min(
                        c[i][j + 1] + 1,
                        c[i + 1][j] + 1,
                        c[i][j] + (0 if m[i] == n[j] else 1)
                    )
                )

        return c[-1][-1]

class CaptchaImageAlgorithm(object):
    '''captcha image algorithm'''
    @staticmethod
    def GetPixelsXEdges(im):
        pixels = im.load()
        xsize, ysize = im.size
        state = -1
        edges = []
        for x in xrange(xsize):
            weight = sum(1 if pixels[x, y] == 0 else 0 for y in xrange(ysize))
            level = 0
            if state == -1 and weight <= level:
                continue
            elif state == 1 and weight > level:
                continue
            else:
                state = -state
                edges.append(x)
        return [(edges[x], edges[x + 1]) for x in range(0, len(edges), 2)]

    @staticmethod
    def GetPixelsYEdges(im):
        pixels = im.load()
        xsize, ysize = im.size
        state = -1
        edges = []
        for y in xrange(ysize):
            weight = sum(1 if pixels[x, y] == 0 else 0 for x in xrange(xsize))
            level = 0
            if state == -1 and weight <= level:
                continue
            elif state == 1 and weight > level:
                continue
            else:
                state = -state
                edges.append(y)
        return [(edges[x], edges[x + 1]) for x in range(0, len(edges), 2)]
  
    @staticmethod
    def StripYEdge(im):
        yedges = CaptchaImageAlgorithm.GetPixelsYEdges(im)
        y1, y2 = yedges[0][0], yedges[-1][1]
        return im.crop((0, y1, im.size[0], y2))

    @staticmethod
    def GetBinaryMap(im):
        xsize, ysize = im.size
        pixels = im.load()
        return '\n'.join(''.join('#' if  pixels[x, y] == 0 else '_' for x in xrange(xsize)) for y in xrange(ysize))
    
    @staticmethod
    def getBitMapIn(im):
        xsize, ysize = im.size
        pixels = im.load()
        return tuple( 0 if  pixels[x, y] == 0 else 255 for x in xrange(xsize) for y in xrange(ysize))

class CaptchaProfile(object):
    
    def __init__(self,features_map):
        self.features_map = features_map
    
    def __new__(cls,features_map):
        '''
                    单态实现，初始化一次 
        '''
        if '_inst' not in vars(cls):
            cls.catagory_FEATURES_MAP__ = dict([(feature_to_data(key),value) for key,value in features_map.iteritems()])
            cls._inst = super(CaptchaProfile, cls).__new__(cls)
        return cls._inst

    def match(self, im):
        #st = time.time()
        imageData = feature_to_data(CaptchaImageAlgorithm.GetBinaryMap(im))     
        result = self.catagory_FEATURES_MAP__.get(imageData,None)
        if result != None:
            return result
        print CaptchaImageAlgorithm.GetBinaryMap(im),'\n'
        source = im.getdata()
        algorithm = CaptchaAlgorithm()
        minimal = min(self.features_map, key=lambda feature:algorithm.LevenshteinDistance(source, feature_to_data(feature)))
        result = self.features_map[minimal]
        self.catagory_FEATURES_MAP__[imageData] = result
        return result
    
    def filter(self, im):
        return im.filter(ImageFilter.EDGE_ENHANCE_MORE).convert('L').convert('1')

    def splitAgorim(self, im, top,bottom):
        xsize, ysize = im.size
        pixels = im.load()
        zeroArr = []
        for x in xrange(xsize):
            flag = True
            for y in xrange(ysize):
                if pixels[x,y] != 255:
                    flag = False
                    break
            if flag or x == 0:
                zeroArr.append(x)
        zeroArr = [(value - index ,value) for index,value in enumerate(zeroArr)]
        retd = []
        for key, group in itertools.groupby(zeroArr, lambda x: x[0]):
            ret = [t[1] for t in group]
            retd.append((ret[0],ret[-1]))
        l = len(retd)
        i = 0
        dd = []
        while i < l - 1 :
            pre = retd[i][1] + 1
            next = retd[i + 1][0]
#            if 2 < next - pre < 7:
#                nPre = retd[i + 1][1]
#                nNext = retd[i + 2][0]
#                if 2 < nNext - nPre < 7:
#                    dd.append((pre,4,nNext,16))
#                    i = i + 2
#                    continue
#            print (pre,4,next,16)
            dd.append((pre,top,next,bottom))
            i = i + 1
        return dd
    
    def split(self, im, top,bottom):
        ddArr = self.splitAgorim(im, top, bottom)
        return (im.crop(idt) for idt in ddArr[1:])
    
def feature_to_data(feature):
    feature = re.sub(r'[\t\s]', '', feature)
    feature = re.sub(r'[\r\n]', '', feature)
    return tuple(0 if x == '#' else 255 for x in feature)

def captcha(filename, profile):
   
    #s = time.time()
    im = Image.open(filename)
    #s2 = time.time()
    #print 'open',s2-s
    im = profile.filter(im)
    #s3 = time.time()
    #print 'filter',s3 - s2 
    im_list = profile.split(im)
    #s4 = time.time()
    #print 'split',s4 - s3
    result = ''.join(profile.match(im) for im in im_list)
    #print 'match',time.time() - s4
    return result

